select可以幫助我們選擇EdgeDB內所有的東西,包括scalar type及object type。
選擇scalar type非常簡單,直接使用select關鍵字即可,如:
select 1;
{1}
我們藉由上述query來討論幾個EdgeDB的細節。
EdgeDB是一個具有嚴格型別規定(strongly typed)的語言,所有的整數如果沒有指定,則隱性定義為int64。我們可以使用is來確認其所屬型別,如:
select 1 is int64;
{true}
而所有的浮點數如果沒有指定,則隱性定義為float64。
select 1.0 is float64;
{true}
當整數與浮點數進行運算後,則會自動轉換為浮點數float64。
select (1 + 1.0) is float64;
{true}
當想要進行型別轉換的時候,可以於前面加上<type>,例如:
select <int16>(1 + 1.0) is int16;
{true}
即可將(1 + 1.0)由float64轉換為int16。
型別轉換並不保證永遠會成功,當轉換失敗時,則會報錯。例如:
select <int16>(32768);
edgedb error: NumericOutOfRangeError: std::int16 out of range
由於int16包含的範圍為-32768到32767,而32768已超出int16涵蓋的範圍,所以報錯out of range。
但是下面這個有趣的query會返回{true},因為<int16>32768的確是顯性指定為int16。
select <int16>(32768) is int16;
{true}
query返回的結果永遠都為EdgeDBSet,其為一無序的multi set,即同一個object可以重覆出現在EdgeDBSet內。例如:
select {1, 1};
{1, 1}
如果返回的結果為空時,則為空EdgeDBSet。而即使是空的EdgeDBSet,其也是具有型態的。也就是說如果您想執行下面這樣的query時,EdgeDB將會報錯,因為無法判斷該EdgeDBSet的型別:
select {};
error: QueryError: expression returns value of indeterminate type
┌─ <query>:1:8
│
1 │ select {};
│ ^^ Consider using an explicit type cast.
必須像下面這樣,顯性指定型別後,才可以成功執行query。
select <int64>{};
{}
此外,EdgeDBSet內所有的object都必須為同一種型別。如果我們試圖建立一個含有int64及str的EdgeDBSet時,會報錯如下:
select 1 union "1";
┌─ <query>:1:8
│
1 │ select 1 union "1";
│ ^^^^^^^^^^^ Consider using an explicit type cast or a conversion function.
但是當執行整數與浮點數的運算時,EdgeDB會自動轉換型別,例如下面這個query將會返回一個其內為1.0及2.0兩個float64型別的EdgeDBSet。
select (1 union 2.0);
{1, 2}
我們可以使用is來確認:
select (1 union 2.0) is float64;
{true, true}
再次強調,由於EdgeDBSet是一無序的multi set,所以您不能斷言第一個true來自判斷1是不是float64型別,而第二個true來自判斷2.0是不是float64型別。
有時候我們需要一些變數來儲存暫時的計算結果,這樣可以使得query看起來更加簡單明瞭。例如:
select (1 + 2) + (3 + 4);
{10}
其中我們特別使用()將1 + 2及3 + 4分別括起來,想要強調此運算將會是由1 + 2與3 + 4相加而成,雖然結果與直接寫成select 1 + 2 + 3 + 4一樣,但含義卻不相同。
此時,我們可以透過with分別定義兩個變數group1及group2後,並於select中使用此兩變數。
with group1:= 1 + 2,
group2:= 3 + 4
select group1 + group2;
{10}
這邊有兩點需要注意:
:=。,隔開。由於在with中,後面的變數可以直接引用前面定義過的變數,所以可以寫出下列的query:
with a:= 1 + 2,
b:= a + 3,
c:= b + 4,
select c;
{10}
這個特性可以讓我們在with中定義一些中間變數,幫助我們在讀query時,更加容易了解其想表達的函義。
我們繼續使用[Day03]中定義的schema:
type User {
required name: str;
multi followers: User;
}
type Article {
required title: str;
required author: User;
}
我們利用nested insert建立一個User及一個Article object。
insert Article {
title:= "first article",
author:= (insert User {name:= "John"})
};
此時我們可以用shape搭配select來顯示所有Article object的title property及author link。
select Article {title, author};
{
default::Article {
title: 'first article',
author: default::User {id: 51d0135a-3f59-11ef-b4b5-e3b7f76b4391}
}
}
但是上述query只顯示了author link所連接的User id,此時我們可以針對author再使用一次shape,即:
select Article {title, author: {name}};
{
default::Article {
title: 'first article',
author: default::User {name: 'John'}
}
}
這樣一來就能看出目前database中只有一個Article object,其有一個title property為「"first article"」及連接一個User object,而該User object內有一個name property為「"John"」。
雖然shape的操作十分方便,但是當一個object具有多個property或link時,也需要花費一番心力。此時EdgeDB提供強大的splat來幫助我們。
針對所選object使用single splat可以顯示該object內所有的property,如:
select Article {*};
{
default::Article {
id: 51d03d6c-3f59-11ef-b4b5-5bee6a32e59e,
title: 'first article'
}
}
如果使用double splat可以顯示該object內所有的property及link,如:
select Article {**};
{
default::Article {
id: 51d03d6c-3f59-11ef-b4b5-5bee6a32e59e,
title: 'first article',
author: default::User {
id: 51d0135a-3f59-11ef-b4b5-e3b7f76b4391,
name: 'John'
},
},
}
這邊需留意,double splats針對link所能擴展的深度僅有一層,也就是說當User object內有link的話,並不會遞迴地擴展下去。
我們利用nested insert建立一個User及一個Article object。
insert Article {
title:= "second article",
author:= (insert User {name:= "May"})
};
這樣一來,當前資料庫內就有兩個User object及Article object。
order by可以幫助我們根據不同準則來排序,例如根據Article object的title property來排:
select Article {title, author:{name}} order by .title;
{
default::Article {
title: 'first article',
author: default::User {name: 'John'}
},
default::Article {
title: 'second article',
author: default::User {name: 'May'}
},
}
因為「"first article"」的「"f"」在英文字母中比「"second article"」的「"s"」還前面,所以title property為「"first article"」的Article object會排在最前。
又或者我們可以使用len()(註1)來計算User object的name property的長度後,再反向排序:
select User {name} order by len(.name) desc;
{default::User {name: 'John'}, default::User {name: 'May'}}
因為「"John"」有四個英文字母比「"May"」的三個還多,所以反向排序後John會出現在May之前。
limit可以讓我們指定由EdgeDBSet中取回幾個元素,例如:
select User {name} limit 1;
{default::User {name: 'John'}}
註1:len()是EdgeDB內建的函數。在EdgeDB中,我們可以定義多個同名的函數,每一個接收不同數量的參數甚至不同多種型別,EdgeDB將會依所傳入的函數數量及型別,來決定回傳值,這樣的特性稱為function overloading。依len()的說明文件來看,其可接受str、bytes及array<anytype>三種型態的輸入,並回傳int64。